package values

import (
	"fmt"
	"math"

	"soluty.cc/go/xmath"

	"soluty.cc/go/xlog"
)

type valueFormula int

const (
	ValueAddThenMulti valueFormula = iota
	ValueMultiThenAdd
)

// 属性的循环依赖 不能存在
// 如果火力 < 100 装甲增加10    b的装甲依赖于a的火力
// 如果装甲 < 100 火力增加10    a的火力依赖于b的装甲
// 假设 火力的优先级 > 装甲  先算所有的火力, 再算所有的装甲
// 95  95  血量百分比   maxhp  attr

// 有两种number计算方式  (1+x%)*base + y   (1+x%)*(base+y)
type Number struct {
	nth          int32 // 进制, 这个字段表示per的percent, 比如100, 那么per就是百分比
	valueFormula valueFormula

	baseValue int32 // 基础值
	midValue  int32 // 中间值
	value     int32 // 最终值

	min int32
	max int32

	modifiers map[modifierTag]*modifierList
}

func (n *Number) Value() int32 {
	return n.value
}

func (n *Number) BaseValue() int32 {
	return n.baseValue
}

func (n *Number) MidValue() int32 {
	return n.midValue
}

func (n *Number) String() string {
	s := fmt.Sprintf("%v|base:%v mid:%v", n.value, n.baseValue, n.midValue)
	for tag, m := range n.modifiers {
		if m.Len() > 0 {
			s = fmt.Sprintf("%v\n	%v: %v", s, tag, m)
		}
	}
	return s
}

func newNumber(nth int32, holder interface{}, minmax ...int32) *Number {
	var min int32 = math.MinInt32
	var max int32 = math.MaxInt32
	if len(minmax) > 0 {
		min = minmax[0]
	}
	if len(minmax) > 1 {
		max = minmax[1]
	}
	n := &Number{
		nth: nth,
		min: min,
		max: max,
		modifiers: map[modifierTag]*modifierList{
			ModifierAdd:         {holder: holder},
			ModifierAddPer:      {holder: holder},
			ModifierFinalAdd:    {holder: holder},
			ModifierFinalAddPer: {holder: holder},
		},
	}
	return n
}

func (n *Number) reset() error {
	n.setFormula(nil)
	err := n.update()
	if err != nil {
		return xlog.Errorf(xlog.Fmt(), "", err)
	}
	return nil
}

func (n *Number) update() error {
	if n.valueFormula == ValueMultiThenAdd {
		return n.updateMultiThenAdd()
	} else if n.valueFormula == ValueAddThenMulti {
		return n.updateAddThenMulti()
	} else {
		return xlog.Errorf("error ValueFormula")
	}
}

func (n *Number) setFormula(formula Formula) {
	for _, m := range n.modifiers {
		m.SetFormula(formula)
	}
}

func (n *Number) SetBase(v int32) {
	if v < n.min {
		v = n.min
	}
	if v > n.max {
		v = n.max
	}
	n.baseValue = v
}

func (n *Number) AddModifier(m *Modifier) {
	n.modifiers[m.Tag].AddModifier(m)
}

func (n *Number) RemoveModifier(m *Modifier) {
	n.modifiers[m.Tag].RemoveModifier(m)
}

func (n *Number) updateMultiThenAdd() error {
	n.midValue = n.baseValue
	n.value = n.baseValue
	for _, m := range n.modifiers {
		err := m.update()
		if err != nil {
			return xlog.Errorf(xlog.Fmt(), "", err)
		}
	}
	value1 := n.baseValue
	perAdd := n.modifiers[ModifierAddPer].totalValue
	p1, err := xmath.Multi(value1, n.nth+perAdd, n.nth)
	if err != nil {
		return xlog.Errorf(xlog.Fmt(), "", err)
	}
	add := n.modifiers[ModifierAdd].totalValue
	value2, err := xmath.Add(p1, add)
	if err != nil {
		return xlog.Errorf(xlog.Fmt(), "", err)
	}
	finalPerAdd := n.modifiers[ModifierFinalAddPer].totalValue
	p2, err := xmath.Multi(value2, n.nth+finalPerAdd, n.nth)
	if err != nil {
		return xlog.Errorf(xlog.Fmt(), "", err)
	}
	finalAdd := n.modifiers[ModifierFinalAdd].totalValue
	value3, err := xmath.Add(p2, finalAdd)
	if err != nil {
		return xlog.Errorf(xlog.Fmt(), "", err)
	}
	if value3 < n.min {
		value3 = n.min
	}
	if value3 > n.max {
		value3 = n.max
	}
	n.value = value3
	if value2 < n.min {
		value2 = n.min
	}
	if value2 > n.max {
		value2 = n.max
	}
	n.midValue = value2
	return nil
}

func (n *Number) updateAddThenMulti() error {
	n.midValue = n.baseValue
	n.value = n.baseValue
	for _, m := range n.modifiers {
		err := m.update()
		if err != nil {
			return xlog.Errorf(xlog.Fmt(), n.baseValue, err)
		}
	}
	value1 := n.baseValue
	add := n.modifiers[ModifierAdd].totalValue
	p1, err := xmath.Add(value1, add)
	if err != nil {
		return xlog.Errorf(xlog.Fmt(), "", err)
	}
	perAdd := n.modifiers[ModifierAddPer].totalValue
	value2, err := xmath.Multi(p1, n.nth+perAdd, n.nth)
	if err != nil {
		return xlog.Errorf(xlog.Fmt(), "", err)
	}
	finalAdd := n.modifiers[ModifierFinalAdd].totalValue
	p2, err := xmath.Add(value2, finalAdd)
	if err != nil {
		return xlog.Errorf(xlog.Fmt(), "", err)
	}
	finalPerAdd := n.modifiers[ModifierFinalAddPer].totalValue
	value3, err := xmath.Multi(p2, n.nth+finalPerAdd, n.nth)
	if err != nil {
		return xlog.Errorf(xlog.Fmt(), "", err)
	}
	if value3 < n.min {
		value3 = n.min
	}
	if value3 > n.max {
		value3 = n.max
	}
	n.value = value3
	if value2 < n.min {
		value2 = n.min
	}
	if value2 > n.max {
		value2 = n.max
	}
	n.midValue = value2
	return nil
}
